[Xcode] ビルドはできるのにストーリーボードではレンダリング反映されないカスタムビュー。agent crashedと言われた時の対処法について
はじめに
CX事業本部の中安です。まいどです。
@IBDesignable class
で定義したビュークラスは、ストーリーボード(またはXIB)上でレンダリングされ、見た目を確認しながらビューコンポーネントを作成していくことが可能です。
しかしながら、ときにストーリーボード上でエラーを吐き出し、レンダリングがされなくなるケースがあります。
表示としては下図のような感じです。
そしてXcode上で表示されるエラーとしては
file:///Users/.../Hoge.storyboard: error: IB Designables: Failed to render and update auto layout status for HogeHogeViewController (XYZ-00-a1c): Failed to launch designables agent because tool was shutting down. Check the console for a more detailed description and please file a bug report at feedbackassistant.apple.com.
または、
Failed to render and update auto layout status, The agent crashed
という表示され、なにやら「agent というのがクラッシュしている?」というのが分かります。
しかもこれの困ったところは、アプリ自体はビルドができてちゃんと正常動作するところです。 こうなってくると、ストーリーボードでレンダリングされないのは何故だ? ということになります。
いつもこのエラーが出ると、毎回苦々しくネットで記事を探すことになるので、今回自分でブログにまとめておこうと思いました。
原因となったもの
まずは、自作のビュークラスが何故このようなエラーを起こしてしまったのかを書いておきましょう。
今回のケースでは、ビュークラスは「バンドル上のJSONファイルを読み込み、そのデータをもとにビューの見た目を決定する」という設計にしていました。
// アプリのバンドルにある view-data.json から ViewData オブジェクトを作り、 // それをビューの見た目に生かそうとしていた private func loadViewDataJson() -> ViewData { do { let url = Bundle.main.url(forResource: "view-data", withExtension: "json")! let data = try Data(contentsOf: url) return try JSONDecoder().decode(ViewData.self, from: data) } catch { fatalError("something wrong...") } }
上の例ではview-data.json
はアプリに埋め込んでいるので、URLオブジェクトを強制アンラップをしているのは間違いではありません(5行目)。
アプリを動かした時にも正常に動作します。
しかし、ストーリーボードでレンダリングされる際には、JSONファイルを置いたメインバンドル内のリソースURLは取得できないというわけです。
@IBDesignable
なビュークラスは、他のリソースに依存するような設計にしてはいけないということですね。
「アプリを実行するのには問題はないが、ストーリーボード上では問題が発生する」という状態の時に、例のエラーは吐き出されるようです。 (ただし、100%とは言えないかもです)
原因の見つけ方
さて、今回はこのように原因が分かったわけですが、エラーが発生するとまずは原因を探る必要があります。 しかし、ソースコード上にエラーが出るわけではないので非常に特定しにくいです。 デバッグ実行ができるわけでもありません。
今ブログでは、その探し方の一例を記しておきたいと思います。
コンソールアプリを開く
Macには標準アプリとして「コンソール」が用意されています。まずはこれを開きます。
Finderではアプリケーション > ユーティリティ > コンソール
で見つかると思います。
Spotlightでは console.app
と入力すると見つかると思います。(cons
くらいでサジェストされると思います)
開かれたら左ペインの「クラッシュレポート」を選び、 検索窓に「IBDesignablesAgent-iOS」と入れておきましょう。 今回見たいIBDesignable関係のレポートだけが絞られると思います。
ビューのレンダリングをリフレッシュする
エラーが発生しているストーリーボードを開き、Xcodeのメニューから Editor > Refresh All Views
を選択して実行します。
コンソールのログを見る
さきほどのコンソールアプリにリフレッシュした時刻くらいのログファイルができていると思います。
その下の方を見ていくと
: (中略) : Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libswiftCore.dylib 0x7fff30585ce7 closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 391 1 libswiftCore.dylib 0x7fff30585a4c closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 300 2 libswiftCore.dylib 0x7fff30585725 closure #1 in _assertionFailure(_:_:file:line:flags:) + 117 3 libswiftCore.dylib 0x7fff3058537a _assertionFailure(_:_:file:line:flags:) + 314 4 SampleApp 0x103ddaea0 SampleApp.loadViewDataJson() + 688
クラッシュ要因となったメソッド名が分かります。 くわしい行数などは出ませんが、およそのエラー位置であるメソッドが分かるだけでアタリをつけられるのではないでしょうか。
まとめ
- IBDesignableなビュークラスを取り扱うときは、他リソースに依存する設計は避けよう
- 「
agent crashed
」とXcode上で怒られたら、コンソールアプリでクラッシュレポートを確認しよう
同じようなエラーに苦しんでる方の助けになれば幸いです。それではまたー